This file is used to analyse the immune cells dataset.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)
library(org.Mm.eg.db)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

save_name = "matrix"
out_dir = "."

We load the dataset :

sobj = readRDS(paste0(out_dir, "/", save_name, "_sobj.rds"))
sobj
## An object of class Seurat 
## 15937 features across 1529 samples within 1 assay 
## Active assay: RNA (15937 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_19_tsne, RNA_pca_19_umap, harmony, harmony_19_umap, harmony_19_tsne

We load the sample information :

sample_info = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_sample_info.rds"))
project_names_oi = sample_info$project_name

graphics::pie(rep(1, nrow(sample_info)),
              col = sample_info$color,
              labels = sample_info$project_name)

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_color_markers.rds"))
color_markers = color_markers[unique(sobj$cluster_type)]

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

This is the projection of interest :

name2D = "harmony_19_tsne"

We design a custom functions to represent cells of interest on the projection :

see_clusters = function(pop_oi = "IRS") {
  # Clusters containing population of interest
  clusters_oi = as.character(unique(sobj$seurat_clusters[sobj$cluster_type == pop_oi]))
  
  # Colors for clusters
  custom_colors = aquarius::gg_color_hue(length(levels(sobj$seurat_clusters)))
  names(custom_colors) = levels(sobj$seurat_clusters)
  custom_colors[!(names(custom_colors) %in% clusters_oi)] = "gray92"
  
  p1 = Seurat::DimPlot(sobj, reduction = name2D, cols = custom_colors,
                       group.by = "seurat_clusters", label = TRUE) +
    ggplot2::labs(title = "Clusters of interest",
                  subtitle = paste0(clusters_oi, collapse = ", ")) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5),
                   plot.subtitle = element_text(hjust = 0.5)) +
    Seurat::NoAxes() + Seurat::NoLegend()
  
  # Color for cell type
  custom_colors = unlist(color_markers)
  custom_colors[!(names(custom_colors) %in% pop_oi)] = "gray92"
  
  p2 = Seurat::DimPlot(sobj, reduction = name2D,
                       group.by = "cluster_type", cols = custom_colors) +
    ggplot2::labs(title = "Annotation of interest",
                  subtitle = pop_oi) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5),
                   plot.subtitle = element_text(hjust = 0.5)) +
    Seurat::NoAxes() + Seurat::NoLegend()
  
  # Piechart for each cluster, by sample name
  plot_list = lapply(clusters_oi, FUN = function(one_cluster) {
    df = sobj@meta.data %>%
      dplyr::filter(.data$seurat_clusters == .env$one_cluster) %>%
      dplyr::select(sample_identifier, seurat_clusters)
    
    p = aquarius::plot_piechart(df,
                                grouping_var = "sample_identifier",
                                colors = sample_info$color) +
      ggplot2::labs(title = paste0("Cluster ", one_cluster),
                    subtitle = paste0(nrow(df), " cells")) +
      ggplot2::theme(plot.title = element_text(hjust = 0.5),
                     plot.subtitle = element_text(hjust = 0.5))
    
    return(p)
  })
  p3 = patchwork::wrap_plots(plot_list)
  
  # Patchwork
  p = patchwork::wrap_plots(p2, p1, p3, nrow = 1)
  
  return(p)
}

We design a custom function to make the GSEA plot and a word cloud graph :

make_gsea_plot = function(gsea_results, gs_oi, fold_change, metric = "FC") {
  fold_change$metric = fold_change[, metric]
  
  plot_list = lapply(gs_oi, FUN = function(gene_set) {
    # Gene set content
    gs_content = gene_sets %>%
      dplyr::filter(gs_name == gene_set) %>%
      dplyr::pull(ensembl_gene) %>%
      unique()
    
    # Gene set size
    nb_genes = length(gs_content)
    
    # Enrichment metrics
    NES = gsea_results@result[gene_set, "NES"]
    p.adjust = gsea_results@result[gene_set, "p.adjust"] %>%
      round(., 4)
    qvalues = gsea_results@result[gene_set, "qvalues"]
    
    if (p.adjust > 0.05) {
      p.adjust = paste0("<span style='color:red;'>", p.adjust, "</span>")
    }
    
    my_subtitle = paste0("\nNES : ", round(NES, 2),
                         " | padj : ", p.adjust,
                         " | qval : ", round(qvalues, 4),
                         " | set size : ", nb_genes, " genes")
    
    # Size limits
    lower_FC = min(fold_change[gs_content, ]$metric, na.rm = TRUE)
    upper_FC = max(fold_change[gs_content, ]$metric, na.rm = TRUE)
    
    # Plot
    p = enrichplot::gseaplot2(x = gsea_results, geneSetID = gene_set) +
      ggplot2::labs(title = gene_set,
                    subtitle = my_subtitle) +
      ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                               margin = ggplot2::margin(3, 3, 5, 3)),
                     plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                              size = 10))
    
    wc = ggplot2::ggplot(fold_change[gs_content, ],
                         aes(label = gene_name, size = abs(metric), color = metric)) +
      ggwordcloud::geom_text_wordcloud_area(show.legend = TRUE) +
      ggplot2::scale_color_gradient2(
        name = metric,
        low = aquarius::color_cnv[1],
        mid = "gray70", midpoint = 0,
        high = aquarius::color_cnv[3]) +
      ggplot2::scale_size_area(max_size = 7) +
      ggplot2::theme_minimal() +
      ggplot2::guides(size = "none")
    
    return(list(p, wc))
  }) %>% unlist(., recursive = FALSE)
  
  return(plot_list)
}

Visualization

Gene expression

We visualize gene expression for some markers :

features = c("percent.mt", "percent.rb", "nFeature_RNA")

plot_list = lapply(features, FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Clusters

We visualize clusters :

cluster_plot = Seurat::DimPlot(sobj, reduction = name2D, label = TRUE) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)
cluster_plot

Cell type

We visualize cell type split by sample :

plot_list = aquarius::plot_split_dimred(sobj,
                                        reduction = name2D,
                                        split_by = "project_name",
                                        group_by = "cell_type",
                                        split_color = setNames(sample_info$color,
                                                               nm = sample_info$project_name),
                                        group_color = color_markers,
                                        bg_pt_size = 0.5, main_pt_size = 0.5)

plot_list[[length(plot_list) + 1]] = cluster_plot

patchwork::wrap_plots(plot_list, ncol = 4) &
  Seurat::NoLegend()

We compare cluster annotation and cell type annotation :

p1 = Seurat::DimPlot(sobj, group.by = "cell_type",
                     reduction = name2D, cols = color_markers) +
  ggplot2::labs(title = "Cell type") +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

p2 = Seurat::DimPlot(sobj, group.by = "cluster_type",
                     reduction = name2D, cols = color_markers) +
  ggplot2::labs(title = "Cluster type") +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5))

patchwork::wrap_plots(p1, p2, guides = "collect")

Differential expression

For each population, what are the differences between healthy donors (HD) and HS patients (HS) ? We save the results in a list :

list_results = list()

We make over-representation analysis for each group of genes. We load gene sets from MSigDB :

gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")
gene_sets = gene_sets$gene_sets

head(gene_sets)
## # A tibble: 6 x 16
##   gs_cat gs_subcat gs_name gene_symbol entrez_gene ensembl_gene human_gene_symb~
##   <chr>  <chr>     <chr>   <chr>             <int> <chr>        <chr>           
## 1 C5     GO:BP     GOBP_1~ AASDHPPT          60496 ENSG0000014~ AASDHPPT        
## 2 C5     GO:BP     GOBP_1~ ALDH1L1           10840 ENSG0000014~ ALDH1L1         
## 3 C5     GO:BP     GOBP_1~ ALDH1L2          160428 ENSG0000013~ ALDH1L2         
## 4 C5     GO:BP     GOBP_1~ MTHFD1             4522 ENSG0000010~ MTHFD1          
## 5 C5     GO:BP     GOBP_1~ MTHFD1L           25902 ENSG0000012~ MTHFD1L         
## 6 C5     GO:BP     GOBP_1~ MTHFD2L          441024 ENSG0000016~ MTHFD2L         
## # ... with 9 more variables: human_entrez_gene <int>, human_ensembl_gene <chr>,
## #   gs_id <chr>, gs_pmid <chr>, gs_geoid <chr>, gs_exact_source <chr>,
## #   gs_url <chr>, gs_description <chr>, category <chr>

How many gene sets ?

gene_sets[, c("gs_subcat", "gs_name")] %>%
  dplyr::distinct() %>%
  dplyr::pull(gs_subcat) %>%
  table() %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Category", "Nb gene sets"))
##          Category Nb gene sets
## 1                           50
## 2         CP:KEGG          186
## 3          CP:PID          196
## 4     CP:REACTOME         1615
## 5 CP:WIKIPATHWAYS          664
## 6           GO:BP         7658
## 7           GO:CC         1006
## 8           GO:MF         1738

We get gene name and gene ID correspondence :

gene_corresp = sobj@assays[["RNA"]]@meta.features[, c("gene_name", "Ensembl_ID")] %>%
  `colnames<-`(c("NAME", "ID")) %>%
  dplyr::mutate(ID = as.character(ID))
rownames(gene_corresp) = gene_corresp$ID

head(gene_corresp)
##                       NAME              ID
## ENSG00000238009 AL627309.1 ENSG00000238009
## ENSG00000237491 AL669831.5 ENSG00000237491
## ENSG00000225880  LINC00115 ENSG00000225880
## ENSG00000230368     FAM41C ENSG00000230368
## ENSG00000230699 AL645608.3 ENSG00000230699
## ENSG00000241180 AL645608.5 ENSG00000241180

Inner root sheath

group_name = "IRS"

IRS are those clusters :

clusters_oi = as.character(unique(sobj$seurat_clusters[sobj$cluster_type == group_name]))
clusters_oi
## [1] "10" "5"

We represent those clusters on the projection :

see_clusters("IRS")

DE

We perform differential expression between HS and HD, within this population :

subsobj = subset(sobj, seurat_clusters %in% clusters_oi)
Seurat::Idents(subsobj) = subsobj$sample_type

table(subsobj$sample_type)
## 
## HS HD 
## 81 67

We make identify specific markers for these group :

mark = Seurat::FindMarkers(subsobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 74  5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## LGALS7B   3.081625e-09 1.5356124 0.938 0.955 4.911186e-05
## LGALS7    4.722986e-11 1.3241540 0.716 0.373 7.527022e-07
## MIF       3.749898e-11 1.0433933 0.914 0.821 5.976212e-07
## MTRNR2L8  2.959636e-13 0.8968284 1.000 0.970 4.716771e-09
## RPS26     7.161254e-15 0.7478794 1.000 1.000 1.141289e-10
## MTRNR2L10 2.790024e-11 0.7066885 0.790 0.507 4.446462e-07
## MTRNR2L12 1.733980e-10 0.6659660 1.000 0.985 2.763443e-06
## ARF5      2.072975e-12 0.6173838 0.654 0.134 3.303701e-08
## EGR1      1.022240e-08 0.5249253 0.914 0.552 1.629144e-04
## KLK7      1.442664e-07 0.4507268 0.728 0.373 2.299173e-03
## SERPINB5  4.901550e-08 0.4480291 0.975 0.985 7.811600e-04
## NBPF14    1.834105e-06 0.4201037 0.716 0.448 2.923013e-02
## S100A11   2.238558e-06 0.3506447 1.000 0.970 3.567590e-02
## MT-CO1    1.886033e-06 0.3498513 1.000 0.985 3.005771e-02
## COMT      1.746678e-06 0.3464609 0.975 0.970 2.783681e-02
## DYNLL1    1.239554e-09 0.3383409 1.000 1.000 1.975478e-05
## PPP4C     9.455785e-07 0.3234715 0.926 0.866 1.506968e-02
## NDUFB3    1.526526e-06 0.2529496 0.988 0.940 2.432825e-02
## OST4      7.834985e-10 0.2521368 0.988 1.000 1.248662e-05
## RPS28     1.035927e-06 0.2517537 1.000 1.000 1.650958e-02

Up in HS

What are the genes up-regulated in HS ?

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in HD

What are the genes up-regulated in HD ?

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(subsobj)[subsobj@active.ident %in% "HS"],
                                            group2 = colnames(subsobj)[subsobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15937     6
##                  gene_name              ID          FC      pct.1      pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009 -0.51845506 0.01234568 0.01492537
## ENSG00000237491 AL669831.5 ENSG00000237491 -0.10341756 0.08641975 0.07462687
## ENSG00000225880  LINC00115 ENSG00000225880  0.13362164 0.11111111 0.07462687
## ENSG00000230368     FAM41C ENSG00000230368 -0.10341756 0.03703704 0.02985075
## ENSG00000230699 AL645608.3 ENSG00000230699 -0.19652696 0.09876543 0.10447761
## ENSG00000241180 AL645608.5 ENSG00000241180  0.06650744 0.02469136 0.01492537
##                     FC_x_pct
## ENSG00000238009 -0.007738135
## ENSG00000237491 -0.007717728
## ENSG00000225880  0.014846849
## ENSG00000230368 -0.003087091
## ENSG00000230699 -0.020532668
## ENSG00000241180  0.001642159

We make the gsea plot for some gene sets :

gs_oi = c("GOBP_KERATINIZATION",
          "GOBP_CELL_AGGREGATION",
          "GOCC_CORNIFIED_ENVELOPE",
          "GOBP_MESENCHYMAL_CELL_DIFFERENTIATION",
          "WP_DNA_REPAIR_PATHWAYS_FULL_NETWORK",
          "HALLMARK_G2M_CHECKPOINT",
          "HALLMARK_E2F_TARGETS")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

Medulla

group_name = "medulla"

Medulla are those clusters :

clusters_oi = as.character(unique(sobj$seurat_clusters[sobj$cluster_type == group_name]))
clusters_oi
## [1] "3" "6" "9"

We represent those clusters on the projection :

see_clusters("medulla")

DE

We perform differential expression between HS and HD, within this population :

subsobj = subset(sobj, seurat_clusters %in% c(2))
Seurat::Idents(subsobj) = subsobj$sample_type

table(subsobj$sample_type)
## 
##  HS  HD 
## 115  62

We make identify specific markers for these group :

mark = Seurat::FindMarkers(subsobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 50  5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## MT4       6.700532e-11 2.3676425 0.852 0.710 1.067864e-06
## LGALS7    1.050898e-11 1.1734249 0.704 0.290 1.674816e-07
## MIF       7.055503e-11 0.9122395 0.826 0.726 1.124435e-06
## MTRNR2L8  1.932382e-14 0.8160014 1.000 0.984 3.079637e-10
## RPS26     2.698863e-14 0.7474198 1.000 1.000 4.301177e-10
## MTRNR2L10 3.527689e-11 0.6690467 0.791 0.468 5.622078e-07
## ARF5      3.299404e-14 0.6154629 0.704 0.129 5.258260e-10
## C1QTNF12  3.570827e-13 0.4589135 1.000 0.887 5.690826e-09
## MTRNR2L12 5.978296e-12 0.4540986 1.000 1.000 9.527610e-08
## NSMCE3    8.961305e-07 0.4219172 0.957 0.871 1.428163e-02
## TOMM5     4.228664e-10 0.4077463 0.678 0.258 6.739222e-06
## EGR1      1.164623e-06 0.4037074 0.991 0.919 1.856059e-02
## GNG11     2.682451e-06 0.3639743 0.722 0.387 4.275023e-02
## MT-CO1    2.889633e-06 0.3516409 1.000 1.000 4.605208e-02
## DYNLL1    2.648089e-11 0.3459339 1.000 0.984 4.220260e-07
## SMS       1.370304e-06 0.3010088 0.983 0.935 2.183854e-02
## NDUFB3    3.024908e-08 0.3004245 0.948 0.935 4.820796e-04
## AKIRIN2   8.224625e-07 0.2962770 0.835 0.661 1.310759e-02
## SRI       1.392941e-06 0.2870494 0.948 0.790 2.219930e-02
## COMT      1.086327e-06 0.2848493 1.000 0.952 1.731280e-02

Up in HS

We explore enrichment in gene sets for HS population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Up-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in HD

We explore enrichment in gene sets for HD population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(subsobj)[subsobj@active.ident %in% "HS"],
                                            group2 = colnames(subsobj)[subsobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 70, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 70)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15937     6
##                  gene_name              ID         FC      pct.1      pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009 -0.4808066 0.01739130 0.01612903
## ENSG00000237491 AL669831.5 ENSG00000237491 -0.1146787 0.21739130 0.16129032
## ENSG00000225880  LINC00115 ENSG00000225880 -0.3653294 0.09565217 0.11290323
## ENSG00000230368     FAM41C ENSG00000230368  1.8411215 0.12173913 0.01612903
## ENSG00000230699 AL645608.3 ENSG00000230699  0.5191934 0.03478261 0.01612903
## ENSG00000241180 AL645608.5 ENSG00000241180  0.5191934 0.01739130 0.00000000
##                     FC_x_pct
## ENSG00000238009 -0.007754945
## ENSG00000237491 -0.018496562
## ENSG00000225880 -0.041246864
## ENSG00000230368  0.224136532
## ENSG00000230699  0.018058901
## ENSG00000241180  0.009029451

We make the gsea plot for some gene sets :

gs_oi = c("GOMF_RAGE_RECEPTOR_BINDING",
          "GOCC_GAMMA_SECRETASE_COMPLEX",
          "REACTOME_NOTCH3_ACTIVATION_AND_TRANSMISSION_OF_SIGNAL_TO_THE_NUCLEUS",
          "REACTOME_NONCANONICAL_ACTIVATION_OF_NOTCH3",
          "GOBP_POSITIVE_REGULATION_OF_CHEMOKINE_C_X_C_MOTIF_LIGAND_2_PRODUCTION",
          "GOBP_POSITIVE_REGULATION_OF_HISTONE_H3_K27_METHYLATION",
          "GOMF_TRANSFORMING_GROWTH_FACTOR_BETA_BINDING",
          "GOMF_STRUCTURAL_MOLECULE_ACTIVITY_CONFERRING_ELASTICITY",
          "GOBP_REGULATION_OF_MESENCHYMAL_STEM_CELL_DIFFERENTIATION")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

Cortex

group_name = "cortex"

Cortex are those clusters :

clusters_oi = as.character(unique(sobj$seurat_clusters[sobj$cluster_type == group_name]))
clusters_oi
## [1] "12" "0"

We represent those clusters on the projection :

see_clusters("cortex")

DE

We perform differential expression between HS and HD, within this population :

subsobj = subset(sobj, seurat_clusters %in% c(0))
Seurat::Idents(subsobj) = subsobj$sample_type

table(subsobj$sample_type)
## 
##  HS  HD 
## 194  97

We make identify specific markers for these group :

mark = Seurat::FindMarkers(subsobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 90  5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## MT4       7.272553e-19 2.5463410 0.856 0.753 1.159027e-14
## LGALS7    9.747132e-23 1.6620818 0.794 0.443 1.553400e-18
## LGALS7B   4.257287e-09 1.3687629 0.928 0.990 6.784838e-05
## MIF       6.101755e-23 1.0505880 0.887 0.784 9.724366e-19
## MTRNR2L8  1.324205e-22 1.0245593 0.933 0.907 2.110385e-18
## KRTAP11-1 7.357915e-10 0.8439837 0.933 0.887 1.172631e-05
## MTRNR2L12 2.169970e-19 0.8137391 0.948 0.928 3.458281e-15
## C10orf99  5.443558e-16 0.7734478 0.742 0.320 8.675398e-12
## TOMM5     2.903241e-25 0.6669403 0.784 0.196 4.626895e-21
## MTRNR2L10 9.299557e-17 0.6493191 0.794 0.526 1.482070e-12
## ARF5      1.110846e-23 0.6491460 0.742 0.155 1.770356e-19
## PRR9      8.804044e-09 0.6337644 1.000 0.990 1.403100e-04
## DAPL1     9.255770e-10 0.5990359 1.000 0.979 1.475092e-05
## RPS26     2.297916e-24 0.5508076 1.000 1.000 3.662189e-20
## MT-CO1    7.910833e-09 0.4960001 0.959 0.938 1.260750e-04
## ALOX15B   3.152614e-10 0.4921725 0.799 0.464 5.024321e-06
## PLCG2     2.052006e-07 0.4913897 0.541 0.227 3.270281e-03
## SERPINB5  3.267773e-12 0.4373238 0.979 0.938 5.207849e-08
## GNG11     6.952996e-08 0.4213139 0.649 0.361 1.108099e-03
## CSTB      2.342789e-13 0.3914171 0.990 1.000 3.733703e-09

Up in HS

We explore enrichment in gene sets for HS population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Up-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in HD

We explore enrichment in gene sets for HD population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(subsobj)[subsobj@active.ident %in% "HS"],
                                            group2 = colnames(subsobj)[subsobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15937     6
##                  gene_name              ID         FC       pct.1      pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009  0.8497322 0.015463918 0.00000000
## ENSG00000237491 AL669831.5 ENSG00000237491  0.8193585 0.201030928 0.10309278
## ENSG00000225880  LINC00115 ENSG00000225880 -0.4721959 0.067010309 0.09278351
## ENSG00000230368     FAM41C ENSG00000230368  1.0976597 0.092783505 0.03092784
## ENSG00000230699 AL645608.3 ENSG00000230699 -0.7352303 0.010309278 0.02061856
## ENSG00000241180 AL645608.5 ENSG00000241180 -1.1502678 0.005154639 0.01030928
##                    FC_x_pct
## ENSG00000238009  0.01314019
## ENSG00000237491  0.16471640
## ENSG00000225880 -0.04381199
## ENSG00000230368  0.10184471
## ENSG00000230699 -0.01515939
## ENSG00000241180 -0.01185843

We make the gsea plot for some gene sets :

gs_oi = c("GOCC_KERATIN_FILAMENT",
          "GOBP_INTERMEDIATE_FILAMENT_ORGANIZATION",
          "WP_CYTOKINES_AND_INFLAMMATORY_RESPONSE",
          "GOBP_POSITIVE_REGULATION_OF_HETEROTYPIC_CELL_CELL_ADHESION",
          "GOBP_RESPONSE_TO_FUNGUS",
          "REACTOME_VOLTAGE_GATED_POTASSIUM_CHANNELS")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

Cuticle

group_name = "cuticle"

Cuticle are those clusters :

clusters_oi = as.character(unique(sobj$seurat_clusters[sobj$cluster_type == group_name]))
clusters_oi
## [1] "4"  "2"  "8"  "7"  "1"  "11"

We represent those clusters on the projection :

see_clusters("cuticle")

DE

We perform differential expression between HS and HD, within this population :

subsobj = subset(sobj, seurat_clusters %in% c(1))
Seurat::Idents(subsobj) = subsobj$sample_type

table(subsobj$sample_type)
## 
##  HS  HD 
## 125  82

We make identify specific markers for these group :

mark = Seurat::FindMarkers(subsobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 40  5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## MT4       1.110113e-08 2.3978461 0.800 0.720 1.769187e-04
## MIF       8.625579e-11 1.0407902 0.792 0.683 1.374659e-06
## LGALS7    5.257608e-11 0.9610409 0.608 0.232 8.379049e-07
## MTRNR2L8  1.233258e-15 0.8365386 0.992 1.000 1.965444e-11
## MTRNR2L12 3.056034e-19 0.7248124 1.000 1.000 4.870402e-15
## RPS26     7.512204e-19 0.6936653 1.000 0.988 1.197220e-14
## MTRNR2L10 1.874930e-10 0.6633563 0.720 0.488 2.988076e-06
## NSMCE3    3.165578e-15 0.6109144 0.960 0.805 5.044981e-11
## KRT25     1.052574e-06 0.5459221 0.784 0.537 1.677487e-02
## S100A10   4.917155e-08 0.4664376 0.992 0.963 7.836470e-04
## TOMM5     1.346169e-10 0.4352497 0.576 0.171 2.145389e-06
## ARF5      1.629853e-08 0.4252033 0.520 0.183 2.597497e-04
## NDUFB3    1.020655e-10 0.3667876 0.952 0.841 1.626617e-06
## C1QTNF12  1.196426e-08 0.3353051 0.976 0.902 1.906744e-04
## C9orf3    2.660042e-09 0.3281363 0.984 0.976 4.239308e-05
## GNG11     1.073711e-06 0.3205391 0.456 0.159 1.711174e-02
## PPP4C     4.056694e-10 0.3193076 0.824 0.549 6.465153e-06
## CSTB      9.455915e-09 0.3179388 0.968 0.976 1.506989e-04
## S100A11   1.609715e-06 0.3148957 0.992 0.963 2.565403e-02
## GSTO1     5.938944e-07 0.2849684 0.968 0.915 9.464895e-03

Up in HS

We explore enrichment in gene sets for HS population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Up-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in HD

We explore enrichment in gene sets for HD population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(subsobj)[subsobj@active.ident %in% "HS"],
                                            group2 = colnames(subsobj)[subsobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(subsobj, assay = "RNA", slot = "counts")[, subsobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15937     6
##                  gene_name              ID         FC pct.1      pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009  1.9954329 0.040 0.00000000
## ENSG00000237491 AL669831.5 ENSG00000237491  0.8510430 0.216 0.12195122
## ENSG00000225880  LINC00115 ENSG00000225880 -0.7415326 0.048 0.09756098
## ENSG00000230368     FAM41C ENSG00000230368  1.1474360 0.056 0.02439024
## ENSG00000230699 AL645608.3 ENSG00000230699 -1.0045671 0.016 0.03658537
## ENSG00000241180 AL645608.5 ENSG00000241180 -0.5895296 0.000 0.00000000
##                    FC_x_pct
## ENSG00000238009  0.07981732
## ENSG00000237491  0.18382530
## ENSG00000225880 -0.07234465
## ENSG00000230368  0.06425642
## ENSG00000230699 -0.03675245
## ENSG00000241180  0.00000000

We make the gsea plot for some gene sets :

gs_oi = c("GOMF_T_CELL_RECEPTOR_BINDING",
          "HALLMARK_PEROXISOME",
          "GOBP_TOLL_LIKE_RECEPTOR_3_SIGNALING_PATHWAY")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

Global visualization

We extract all DE genes :

features_oi = lapply(list_results, `[[`, 1) %>%
  lapply(., FUN = rownames) %>%
  unlist() %>% unname() %>% unique()

length(features_oi)
## [1] 138

Heatmap

We prepare the scaled expression matrix :

mat_expression = Seurat::GetAssayData(sobj, assay = "RNA", slot = "data")[features_oi, ]
mat_expression = Matrix::t(mat_expression)
mat_expression = dynutils::scale_quantile(mat_expression) # between 0 and 1
mat_expression = Matrix::t(mat_expression)
mat_expression = as.matrix(mat_expression) # not sparse

dim(mat_expression)
## [1]  138 1529

We prepare the heatmap annotation :

ha_top = ComplexHeatmap::HeatmapAnnotation(
  cell_type = sobj$cluster_type,
  sample_type = sobj$sample_type,
  cluster = sobj$seurat_clusters,
  col = list(cell_type = color_markers,
             sample_type = setNames(nm = c("HS", "HD"),
                                    c("#C55F40", "#2C78E6")),
             cluster = setNames(nm = levels(sobj$seurat_clusters),
                                aquarius::gg_color_hue(length(levels(sobj$seurat_clusters))))))

And the heatmap :

sobj$cell_group = paste0(sobj$cluster_type, sobj$sample_type) %>%
  as.factor()

ht = ComplexHeatmap::Heatmap(mat_expression,
                             col = aquarius::color_cnv,
                             # Annotation
                             top_annotation = ha_top,
                             # Grouping
                             column_order = sobj@meta.data %>%
                               dplyr::arrange(cluster_type, sample_type, seurat_clusters) %>%
                               rownames(),
                             column_split = sobj$cell_group,
                             column_gap = rep(unit(c(0.01, 2), "mm"), 4),
                             column_title = NULL,
                             cluster_rows = FALSE,
                             cluster_columns = FALSE,
                             show_column_names = FALSE,
                             # Visual aspect
                             show_heatmap_legend = TRUE,
                             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Violin plots

plot_list = lapply(sort(features_oi), FUN = function(one_gene) {
  p = Seurat::VlnPlot(sobj, group.by = "cluster_type", split.by = "sample_type",
                      features = one_gene) +
    ggplot2::scale_fill_manual(breaks = c("HS", "HD"),
                               values = c("#C55F40", "#2C78E6")) +
    ggplot2::theme(plot.subtitle = element_text(hjust = 0.5),
                   axis.title.x = element_blank(),
                   legend.position = "none")
  return(p)
})

names(plot_list) = sort(features_oi)
patchwork::wrap_plots(plot_list, ncol = 6)

Gamma secretase

Gamma secratase coding-genes are enriched in the cortex, in HS compared to HD. We make a score for all cells for the associated gene set.

the_gs_name = "GOCC_GAMMA_SECRETASE_COMPLEX"
the_content = gene_sets %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unique()

sobj$score_gamma = Seurat::AddModuleScore(sobj,
                                          features = list(the_content))$Cluster1

the_content
## [1] "APH1A"  "APH1B"  "NCSTN"  "PSEN1"  "PSEN2"  "PSENEN" "TMED10"

We look at the score split by cell type and sample type.

Seurat::VlnPlot(sobj, group.by = "cluster_type", split.by = "sample_type",
                features = "score_gamma", pt.size = 0.001) +
  ggplot2::labs(title = the_gs_name) +
  ggplot2::scale_fill_manual(breaks = c("HS", "HD"),
                               values = c("#C55F40", "#2C78E6")) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5),
                 axis.title.x = element_blank())

Save

We save the list of results :

saveRDS(list_results, file = paste0(out_dir, "/", save_name, "_list_results.rds"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
##  [1] parallel  stats4    grid      stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] org.Mm.eg.db_3.10.0   AnnotationDbi_1.48.0  IRanges_2.20.2       
##  [4] S4Vectors_0.24.4      Biobase_2.46.0        BiocGenerics_0.32.0  
##  [7] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [10] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              scater_1.14.6              
##  [33] irlba_2.3.3                 markdown_1.1               
##  [35] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [37] Rcpp_1.0.9                  readr_2.0.2                
##  [39] KernSmooth_2.23-17          carrier_0.1.0              
##  [41] promises_1.1.0              gdata_2.18.0               
##  [43] DelayedArray_0.12.3         limma_3.42.2               
##  [45] graph_1.64.0                RcppParallel_5.1.4         
##  [47] Hmisc_4.4-0                 fs_1.5.2                   
##  [49] RSpectra_0.16-0             fastmatch_1.1-0            
##  [51] ranger_0.12.1               digest_0.6.25              
##  [53] png_0.1-7                   sctransform_0.2.1          
##  [55] cowplot_1.0.0               DOSE_3.12.0                
##  [57] here_1.0.1                  TInGa_0.0.0.9000           
##  [59] ggraph_2.0.3                pkgconfig_2.0.3            
##  [61] GO.db_3.10.0                DelayedMatrixStats_1.8.0   
##  [63] gower_0.2.1                 ggbeeswarm_0.6.0           
##  [65] iterators_1.0.12            DropletUtils_1.6.1         
##  [67] reticulate_1.26             clusterProfiler_3.14.3     
##  [69] SummarizedExperiment_1.16.1 circlize_0.4.15            
##  [71] beeswarm_0.4.0              GetoptLong_1.0.5           
##  [73] xfun_0.35                   bslib_0.3.1                
##  [75] zoo_1.8-10                  tidyselect_1.1.0           
##  [77] reshape2_1.4.4              purrr_0.3.4                
##  [79] ica_1.0-2                   pcaPP_1.9-73               
##  [81] viridisLite_0.3.0           rtracklayer_1.46.0         
##  [83] rlang_1.0.2                 hexbin_1.28.1              
##  [85] jquerylib_0.1.4             dyneval_0.9.9              
##  [87] glue_1.4.2                  RColorBrewer_1.1-2         
##  [89] matrixStats_0.56.0          stringr_1.4.0              
##  [91] lava_1.6.7                  europepmc_0.3              
##  [93] DESeq2_1.26.0               recipes_0.1.17             
##  [95] labeling_0.3                httpuv_1.5.2               
##  [97] class_7.3-17                BiocNeighbors_1.4.2        
##  [99] DO.db_2.9                   annotate_1.64.0            
## [101] jsonlite_1.7.2              XVector_0.26.0             
## [103] bit_4.0.4                   mime_0.9                   
## [105] aquarius_0.1.5              Rsamtools_2.2.3            
## [107] gridExtra_2.3               gplots_3.0.3               
## [109] stringi_1.4.6               processx_3.5.2             
## [111] gsl_2.1-6                   bitops_1.0-6               
## [113] cli_3.0.1                   batchelor_1.2.4            
## [115] RSQLite_2.2.0               randomForest_4.6-14        
## [117] tidyr_1.1.4                 data.table_1.14.2          
## [119] rstudioapi_0.13             GenomicAlignments_1.22.1   
## [121] nlme_3.1-147                qvalue_2.18.0              
## [123] scran_1.14.6                locfit_1.5-9.4             
## [125] scDblFinder_1.1.8           listenv_0.8.0              
## [127] ggthemes_4.2.4              gridGraphics_0.5-0         
## [129] R.oo_1.24.0                 dbplyr_1.4.4               
## [131] TTR_0.24.2                  readxl_1.3.1               
## [133] lifecycle_1.0.1             timeDate_3043.102          
## [135] ggpattern_0.3.1             munsell_0.5.0              
## [137] cellranger_1.1.0            R.methodsS3_1.8.1          
## [139] proxyC_0.1.5                visNetwork_2.0.9           
## [141] caTools_1.18.0              codetools_0.2-16           
## [143] ggwordcloud_0.5.0           GenomeInfoDb_1.22.1        
## [145] vipor_0.4.5                 lmtest_0.9-38              
## [147] msigdbr_7.5.1               htmlTable_1.13.3           
## [149] triebeard_0.3.0             lsei_1.2-0                 
## [151] xtable_1.8-4                ROCR_1.0-7                 
## [153] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [155] abind_1.4-5                 farver_2.0.3               
## [157] parallelly_1.28.1           RANN_2.6.1                 
## [159] askpass_1.1                 GenomicRanges_1.38.0       
## [161] RcppAnnoy_0.0.16            tibble_3.1.5               
## [163] ggdendro_0.1-20             cluster_2.1.0              
## [165] future.apply_1.5.0          Seurat_3.1.5               
## [167] dendextend_1.15.1           Matrix_1.3-2               
## [169] ellipsis_0.3.2              prettyunits_1.1.1          
## [171] lubridate_1.7.9             ggridges_0.5.2             
## [173] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [175] fgsea_1.12.0                remotes_2.4.2              
## [177] scBFA_1.0.0                 destiny_3.0.1              
## [179] VIM_6.1.1                   testthat_3.1.0             
## [181] htmltools_0.5.2             BiocFileCache_1.10.2       
## [183] yaml_2.2.1                  utf8_1.1.4                 
## [185] plotly_4.9.2.1              XML_3.99-0.3               
## [187] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [189] foreign_0.8-76              withr_2.5.0                
## [191] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [193] xgboost_1.4.1.1             bit64_4.0.5                
## [195] foreach_1.5.0               robustbase_0.93-9          
## [197] Biostrings_2.54.0           GOSemSim_2.13.1            
## [199] rsvd_1.0.3                  memoise_2.0.0              
## [201] evaluate_0.18               forcats_0.5.0              
## [203] rio_0.5.16                  geneplotter_1.64.0         
## [205] tzdb_0.1.2                  caret_6.0-86               
## [207] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [209] curl_4.3                    fdrtool_1.2.15             
## [211] fansi_0.4.1                 highr_0.8                  
## [213] urltools_1.7.3              xts_0.12.1                 
## [215] GSEABase_1.48.0             acepack_1.4.1              
## [217] edgeR_3.28.1                checkmate_2.0.0            
## [219] scds_1.2.0                  cachem_1.0.6               
## [221] npsurv_0.4-0                babelgene_22.3             
## [223] rjson_0.2.20                openxlsx_4.1.5             
## [225] ggrepel_0.9.1               clue_0.3-60                
## [227] rprojroot_2.0.2             stabledist_0.7-1           
## [229] tools_3.6.3                 sass_0.4.0                 
## [231] nichenetr_1.1.1             magrittr_2.0.1             
## [233] RCurl_1.98-1.2              proxy_0.4-24               
## [235] car_3.0-11                  ape_5.3                    
## [237] ggplotify_0.0.5             xml2_1.3.2                 
## [239] httr_1.4.2                  assertthat_0.2.1           
## [241] rmarkdown_2.18              boot_1.3-25                
## [243] globals_0.14.0              R6_2.4.1                   
## [245] Rhdf5lib_1.8.0              nnet_7.3-14                
## [247] RcppHNSW_0.2.0              progress_1.2.2             
## [249] genefilter_1.68.0           statmod_1.4.34             
## [251] gtools_3.8.2                shape_1.4.6                
## [253] HDF5Array_1.14.4            BiocSingular_1.2.2         
## [255] rhdf5_2.30.1                splines_3.6.3              
## [257] AUCell_1.8.0                carData_3.0-4              
## [259] colorspace_1.4-1            generics_0.1.0             
## [261] base64enc_0.1-3             dynfeature_1.0.0           
## [263] smoother_1.1                gridtext_0.1.1             
## [265] pillar_1.6.3                tweenr_1.0.1               
## [267] sp_1.4-1                    ggplot.multistats_1.0.0    
## [269] rvcheck_0.1.8               GenomeInfoDbData_1.2.2     
## [271] plyr_1.8.6                  gtable_0.3.0               
## [273] zip_2.2.0                   knitr_1.41                 
## [275] latticeExtra_0.6-29         biomaRt_2.42.1             
## [277] fastmap_1.1.0               ADGofTest_0.3              
## [279] copula_1.0-0                doParallel_1.0.15          
## [281] vcd_1.4-8                   babelwhale_1.0.1           
## [283] openssl_1.4.1               scales_1.1.1               
## [285] backports_1.2.1             ipred_0.9-12               
## [287] enrichplot_1.6.1            hms_1.1.1                  
## [289] ggforce_0.3.1               Rtsne_0.15                 
## [291] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [293] polyclip_1.10-0             lazyeval_0.2.2             
## [295] Formula_1.2-3               tsne_0.1-3                 
## [297] crayon_1.3.4                MASS_7.3-54                
## [299] pROC_1.16.2                 viridis_0.5.1              
## [301] dynparam_1.0.0              rpart_4.1-15               
## [303] zinbwave_1.8.0              compiler_3.6.3             
## [305] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJab29tIGluIG1hdHJpeCBjZWxscyIKYXV0aG9yOiAiQXVkcmV5IgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclWS0lbS0lZCcpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCjwhLS0gQXV0b21hdGljYWxseSBjb21wdXRlcyBhbmQgcHJpbnRzIGluIHRoZSBvdXRwdXQgdGhlIHJ1bm5pbmcgdGltZSBmb3IgYW55IGNvZGUgY2h1bmsgLS0+CmBgYHtyLCBlY2hvPUZBTFNFfQojIGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL3JtYXJrZG93bi9pc3N1ZXMvMTQ1Mwpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZF8iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+IiwgInNob3ciLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IiksCiAgdGltZV9pdCA9IGxvY2FsKHsKICAgIG5vdyA9IE5VTEwKICAgIGZ1bmN0aW9uKGJlZm9yZSwgb3B0aW9ucykgewogICAgICBpZiAob3B0aW9ucyR0aW1lX2l0KSB7CiAgICAgICAgaWYgKGJlZm9yZSkgewogICAgICAgICAgbm93IDw9IFN5cy50aW1lKCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmVzID0gZGlmZnRpbWUoU3lzLnRpbWUoKSwgbm93LCB1bml0cyA9ICJzZWNzIikKICAgICAgICAgIHBhc3RlKCIoVGltZSB0byBydW4gOiIsIHJvdW5kKHJlcywgZGlnaXRzID0gMiksICJzKSIpCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfSkKKQpgYGAKCjwhLS0gU2V0IGRlZmF1bHQgcGFyYW1ldGVycyBmb3IgYWxsIGNodW5rcyAtLT4KYGBge3IsIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CnNldC5zZWVkKDEzMzdMKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICMgZGlzcGxheSBjb2RlCiAgICAgICAgICAgICAgICAgICAgICAjIGRpc3BsYXkgY2h1bmsgb3V0cHV0CiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX291dHB1dCA9IEZBTFNFLCAjIHVzZWZ1bGwgZm9yIHNlc3Npb25JbmZvKCkKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfcGxvdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIGZpZ3VyZSBzZXR0aW5ncwogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSAyMCwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSAxNSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzb21ldGhpbmcgYWJvdXQgc2VlZCwgY2h1bmsgYW5kIFJtYXJrZG93biBjb21waWxhdGlvbgogICAgICAgICAgICAgICAgICAgICAgIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zOTQxNzAwMy9sb25nLXZlY3RvcnMtbm90LXN1cHBvcnRlZC15ZXQtZXJyb3ItaW4tcm1kLWJ1dC1ub3QtaW4tci1zY3JpcHQKICAgICAgICAgICAgICAgICAgICAgICMgY2FjaGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUubGF6eSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBhZGQgcnVudGltZSBhZnRlciBjaHVuawogICAgICAgICAgICAgICAgICAgICAgdGltZV9pdCA9IEZBTFNFKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBhbmFseXNlIHRoZSBpbW11bmUgY2VsbHMgZGF0YXNldC4KCmBgYHtyIGxpYnJhcnl9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkob3JnLk1tLmVnLmRiKQoKLmxpYlBhdGhzKCkKYGBgCgojIFByZXBhcmF0aW9uCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHNldCB0aGUgZ2xvYmFsIHNldHRpbmdzIG9mIHRoZSBhbmFseXNpcy4gV2Ugd2lsbCBzdG9yZSBkYXRhIHRoZXJlIDoKCmBgYHtyIG91dF9kaXJ9CnNhdmVfbmFtZSA9ICJtYXRyaXgiCm91dF9kaXIgPSAiLiIKYGBgCgpXZSBsb2FkIHRoZSBkYXRhc2V0IDoKCmBgYHtyIGxvYWRfc29ian0Kc29iaiA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX3NvYmoucmRzIikpCnNvYmoKYGBgCgpXZSBsb2FkIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gOgoKYGBge3IgY3VzdG9tX3BhbGV0dGVfc2FtcGxlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0Kc2FtcGxlX2luZm8gPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLy4uLy4uLzFfbWV0YWRhdGEvaHNfaGRfc2FtcGxlX2luZm8ucmRzIikpCnByb2plY3RfbmFtZXNfb2kgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUKCmdyYXBoaWNzOjpwaWUocmVwKDEsIG5yb3coc2FtcGxlX2luZm8pKSwKICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICBsYWJlbHMgPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpCmBgYAoKSGVyZSBhcmUgY3VzdG9tIGNvbG9ycyBmb3IgZWFjaCBjZWxsIHR5cGUgOgoKYGBge3IgY29sb3JfbWFya2VycywgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY29sb3JfbWFya2VycyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vLi4vMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1t1bmlxdWUoc29iaiRjbHVzdGVyX3R5cGUpXQoKZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSkpCmBgYAoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBvZiBpbnRlcmVzdCA6CgpgYGB7ciBuYW1lMkR9Cm5hbWUyRCA9ICJoYXJtb255XzE5X3RzbmUiCmBgYAoKV2UgZGVzaWduIGEgY3VzdG9tIGZ1bmN0aW9ucyB0byByZXByZXNlbnQgY2VsbHMgb2YgaW50ZXJlc3Qgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX2NsdXN0ZXJzLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2VlX2NsdXN0ZXJzID0gZnVuY3Rpb24ocG9wX29pID0gIklSUyIpIHsKICAjIENsdXN0ZXJzIGNvbnRhaW5pbmcgcG9wdWxhdGlvbiBvZiBpbnRlcmVzdAogIGNsdXN0ZXJzX29pID0gYXMuY2hhcmFjdGVyKHVuaXF1ZShzb2JqJHNldXJhdF9jbHVzdGVyc1tzb2JqJGNsdXN0ZXJfdHlwZSA9PSBwb3Bfb2ldKSkKICAKICAjIENvbG9ycyBmb3IgY2x1c3RlcnMKICBjdXN0b21fY29sb3JzID0gYXF1YXJpdXM6OmdnX2NvbG9yX2h1ZShsZW5ndGgobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSkpCiAgbmFtZXMoY3VzdG9tX2NvbG9ycykgPSBsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpCiAgY3VzdG9tX2NvbG9yc1shKG5hbWVzKGN1c3RvbV9jb2xvcnMpICVpbiUgY2x1c3RlcnNfb2kpXSA9ICJncmF5OTIiCiAgCiAgcDEgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBjb2xzID0gY3VzdG9tX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBsYWJlbCA9IFRSVUUpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2x1c3RlcnMgb2YgaW50ZXJlc3QiLAogICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMChjbHVzdGVyc19vaSwgY29sbGFwc2UgPSAiLCAiKSkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKICAKICAjIENvbG9yIGZvciBjZWxsIHR5cGUKICBjdXN0b21fY29sb3JzID0gdW5saXN0KGNvbG9yX21hcmtlcnMpCiAgY3VzdG9tX2NvbG9yc1shKG5hbWVzKGN1c3RvbV9jb2xvcnMpICVpbiUgcG9wX29pKV0gPSAiZ3JheTkyIgogIAogIHAyID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY3VzdG9tX2NvbG9ycykgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJBbm5vdGF0aW9uIG9mIGludGVyZXN0IiwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwb3Bfb2kpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCiAgCiAgIyBQaWVjaGFydCBmb3IgZWFjaCBjbHVzdGVyLCBieSBzYW1wbGUgbmFtZQogIHBsb3RfbGlzdCA9IGxhcHBseShjbHVzdGVyc19vaSwgRlVOID0gZnVuY3Rpb24ob25lX2NsdXN0ZXIpIHsKICAgIGRmID0gc29iakBtZXRhLmRhdGEgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIoLmRhdGEkc2V1cmF0X2NsdXN0ZXJzID09IC5lbnYkb25lX2NsdXN0ZXIpICU+JQogICAgICBkcGx5cjo6c2VsZWN0KHNhbXBsZV9pZGVudGlmaWVyLCBzZXVyYXRfY2x1c3RlcnMpCiAgICAKICAgIHAgPSBhcXVhcml1czo6cGxvdF9waWVjaGFydChkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cGluZ192YXIgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXN0ZTAoIkNsdXN0ZXIgIiwgb25lX2NsdXN0ZXIpLAogICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5yb3coZGYpLCAiIGNlbGxzIikpICsKICAgICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQogICAgCiAgICByZXR1cm4ocCkKICB9KQogIHAzID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkKICAKICAjIFBhdGNod29yawogIHAgPSBwYXRjaHdvcms6OndyYXBfcGxvdHMocDIsIHAxLCBwMywgbnJvdyA9IDEpCiAgCiAgcmV0dXJuKHApCn0KYGBgCgpXZSBkZXNpZ24gYSBjdXN0b20gZnVuY3Rpb24gdG8gbWFrZSB0aGUgR1NFQSBwbG90IGFuZCBhIHdvcmQgY2xvdWQgZ3JhcGggOgoKYGBge3IgbWFrZV9nc2VhX3Bsb3QsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQptYWtlX2dzZWFfcGxvdCA9IGZ1bmN0aW9uKGdzZWFfcmVzdWx0cywgZ3Nfb2ksIGZvbGRfY2hhbmdlLCBtZXRyaWMgPSAiRkMiKSB7CiAgZm9sZF9jaGFuZ2UkbWV0cmljID0gZm9sZF9jaGFuZ2VbLCBtZXRyaWNdCiAgCiAgcGxvdF9saXN0ID0gbGFwcGx5KGdzX29pLCBGVU4gPSBmdW5jdGlvbihnZW5lX3NldCkgewogICAgIyBHZW5lIHNldCBjb250ZW50CiAgICBnc19jb250ZW50ID0gZ2VuZV9zZXRzICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gZ2VuZV9zZXQpICU+JQogICAgICBkcGx5cjo6cHVsbChlbnNlbWJsX2dlbmUpICU+JQogICAgICB1bmlxdWUoKQogICAgCiAgICAjIEdlbmUgc2V0IHNpemUKICAgIG5iX2dlbmVzID0gbGVuZ3RoKGdzX2NvbnRlbnQpCiAgICAKICAgICMgRW5yaWNobWVudCBtZXRyaWNzCiAgICBORVMgPSBnc2VhX3Jlc3VsdHNAcmVzdWx0W2dlbmVfc2V0LCAiTkVTIl0KICAgIHAuYWRqdXN0ID0gZ3NlYV9yZXN1bHRzQHJlc3VsdFtnZW5lX3NldCwgInAuYWRqdXN0Il0gJT4lCiAgICAgIHJvdW5kKC4sIDQpCiAgICBxdmFsdWVzID0gZ3NlYV9yZXN1bHRzQHJlc3VsdFtnZW5lX3NldCwgInF2YWx1ZXMiXQogICAgCiAgICBpZiAocC5hZGp1c3QgPiAwLjA1KSB7CiAgICAgIHAuYWRqdXN0ID0gcGFzdGUwKCI8c3BhbiBzdHlsZT0nY29sb3I6cmVkOyc+IiwgcC5hZGp1c3QsICI8L3NwYW4+IikKICAgIH0KICAgIAogICAgbXlfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQoTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICIgfCBwYWRqIDogIiwgcC5hZGp1c3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAiIHwgcXZhbCA6ICIsIHJvdW5kKHF2YWx1ZXMsIDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIiB8IHNldCBzaXplIDogIiwgbmJfZ2VuZXMsICIgZ2VuZXMiKQogICAgCiAgICAjIFNpemUgbGltaXRzCiAgICBsb3dlcl9GQyA9IG1pbihmb2xkX2NoYW5nZVtnc19jb250ZW50LCBdJG1ldHJpYywgbmEucm0gPSBUUlVFKQogICAgdXBwZXJfRkMgPSBtYXgoZm9sZF9jaGFuZ2VbZ3NfY29udGVudCwgXSRtZXRyaWMsIG5hLnJtID0gVFJVRSkKICAgIAogICAgIyBQbG90CiAgICBwID0gZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBnc2VhX3Jlc3VsdHMsIGdlbmVTZXRJRCA9IGdlbmVfc2V0KSArCiAgICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lX3NldCwKICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IG15X3N1YnRpdGxlKSArCiAgICAgIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQogICAgCiAgICB3YyA9IGdncGxvdDI6OmdncGxvdChmb2xkX2NoYW5nZVtnc19jb250ZW50LCBdLAogICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gZ2VuZV9uYW1lLCBzaXplID0gYWJzKG1ldHJpYyksIGNvbG9yID0gbWV0cmljKSkgKwogICAgICBnZ3dvcmRjbG91ZDo6Z2VvbV90ZXh0X3dvcmRjbG91ZF9hcmVhKHNob3cubGVnZW5kID0gVFJVRSkgKwogICAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudDIoCiAgICAgICAgbmFtZSA9IG1ldHJpYywKICAgICAgICBsb3cgPSBhcXVhcml1czo6Y29sb3JfY252WzFdLAogICAgICAgIG1pZCA9ICJncmF5NzAiLCBtaWRwb2ludCA9IDAsCiAgICAgICAgaGlnaCA9IGFxdWFyaXVzOjpjb2xvcl9jbnZbM10pICsKICAgICAgZ2dwbG90Mjo6c2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNykgKwogICAgICBnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkgKwogICAgICBnZ3Bsb3QyOjpndWlkZXMoc2l6ZSA9ICJub25lIikKICAgIAogICAgcmV0dXJuKGxpc3QocCwgd2MpKQogIH0pICU+JSB1bmxpc3QoLiwgcmVjdXJzaXZlID0gRkFMU0UpCiAgCiAgcmV0dXJuKHBsb3RfbGlzdCkKfQpgYGAKCiMgVmlzdWFsaXphdGlvbgoKIyMgR2VuZSBleHByZXNzaW9uCgpXZSB2aXN1YWxpemUgZ2VuZSBleHByZXNzaW9uIGZvciBzb21lIG1hcmtlcnMgOgoKYGBge3IgcGxvdF9saXN0X2ZlYXR1cmVzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmZlYXR1cmVzID0gYygicGVyY2VudC5tdCIsICJwZXJjZW50LnJiIiwgIm5GZWF0dXJlX1JOQSIpCgpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXMsIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJEKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCiMjIENsdXN0ZXJzCgpXZSB2aXN1YWxpemUgY2x1c3RlcnMgOgoKYGBge3Igc2VlX2NsdXN0ZXJpbmcsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpjbHVzdGVyX3Bsb3QgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBsYWJlbCA9IFRSVUUpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpjbHVzdGVyX3Bsb3QKYGBgCgojIyBDZWxsIHR5cGUKCldlIHZpc3VhbGl6ZSBjZWxsIHR5cGUgc3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIHBsb3Rfc3BsaXRfZGltcmVkLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDd9CnBsb3RfbGlzdCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSAicHJvamVjdF9uYW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9jb2xvciA9IHNldE5hbWVzKHNhbXBsZV9pbmZvJGNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMC41LCBtYWluX3B0X3NpemUgPSAwLjUpCgpwbG90X2xpc3RbW2xlbmd0aChwbG90X2xpc3QpICsgMV1dID0gY2x1c3Rlcl9wbG90CgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gNCkgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCldlIGNvbXBhcmUgY2x1c3RlciBhbm5vdGF0aW9uIGFuZCBjZWxsIHR5cGUgYW5ub3RhdGlvbiA6CgpgYGB7ciBzZWVfY2x1c3Rlcl90eXBlLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9CnAxID0gU2V1cmF0OjpEaW1QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkNlbGwgdHlwZSIpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKcDIgPSBTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gbmFtZTJELCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiQ2x1c3RlciB0eXBlIikgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocDEsIHAyLCBndWlkZXMgPSAiY29sbGVjdCIpCmBgYAoKIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKRm9yIGVhY2ggcG9wdWxhdGlvbiwgd2hhdCBhcmUgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gaGVhbHRoeSBkb25vcnMgKEhEKSBhbmQgSFMgcGF0aWVudHMgKEhTKSA/IFdlIHNhdmUgdGhlIHJlc3VsdHMgaW4gYSBsaXN0IDoKCmBgYHtyIGxpc3RfcmVzdWx0c30KbGlzdF9yZXN1bHRzID0gbGlzdCgpCmBgYAoKV2UgbWFrZSBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIGZvciBlYWNoIGdyb3VwIG9mIGdlbmVzLiBXZSBsb2FkIGdlbmUgc2V0cyBmcm9tIE1TaWdEQiA6CgpgYGB7ciBnZW5lX3NldHN9CmdlbmVfc2V0cyA9IGFxdWFyaXVzOjpnZXRfZ2VuZV9zZXRzKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIikKZ2VuZV9zZXRzID0gZ2VuZV9zZXRzJGdlbmVfc2V0cwoKaGVhZChnZW5lX3NldHMpCmBgYAoKSG93IG1hbnkgZ2VuZSBzZXRzID8KCmBgYHtyIGNvdW50X2dlbmVfc2V0c30KZ2VuZV9zZXRzWywgYygiZ3Nfc3ViY2F0IiwgImdzX25hbWUiKV0gJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6OnB1bGwoZ3Nfc3ViY2F0KSAlPiUKICB0YWJsZSgpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiQ2F0ZWdvcnkiLCAiTmIgZ2VuZSBzZXRzIikpCmBgYAoKV2UgZ2V0IGdlbmUgbmFtZSBhbmQgZ2VuZSBJRCBjb3JyZXNwb25kZW5jZSA6CgpgYGB7ciBnZW5lX2NvcnJlc3B9CmdlbmVfY29ycmVzcCA9IHNvYmpAYXNzYXlzW1siUk5BIl1dQG1ldGEuZmVhdHVyZXNbLCBjKCJnZW5lX25hbWUiLCAiRW5zZW1ibF9JRCIpXSAlPiUKICBgY29sbmFtZXM8LWAoYygiTkFNRSIsICJJRCIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKElEID0gYXMuY2hhcmFjdGVyKElEKSkKcm93bmFtZXMoZ2VuZV9jb3JyZXNwKSA9IGdlbmVfY29ycmVzcCRJRAoKaGVhZChnZW5lX2NvcnJlc3ApCmBgYAoKIyMgSW5uZXIgcm9vdCBzaGVhdGgKCmBgYHtyIGdyb3VwX25hbWVfSVJTfQpncm91cF9uYW1lID0gIklSUyIKYGBgCgpJUlMgYXJlIHRob3NlIGNsdXN0ZXJzIDoKCmBgYHtyIGNsdXN0ZXJzX29pX0lSU30KY2x1c3RlcnNfb2kgPSBhcy5jaGFyYWN0ZXIodW5pcXVlKHNvYmokc2V1cmF0X2NsdXN0ZXJzW3NvYmokY2x1c3Rlcl90eXBlID09IGdyb3VwX25hbWVdKSkKY2x1c3RlcnNfb2kKYGBgCgpXZSByZXByZXNlbnQgdGhvc2UgY2x1c3RlcnMgb24gdGhlIHByb2plY3Rpb24gOgoKYGBge3Igc2VlX0lSUywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA1fQpzZWVfY2x1c3RlcnMoIklSUyIpCmBgYAoKIyMjIERFCgpXZSBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gSFMgYW5kIEhELCB3aXRoaW4gdGhpcyBwb3B1bGF0aW9uIDoKCmBgYHtyIHN1YnNldF9JUlMsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQpzdWJzb2JqID0gc3Vic2V0KHNvYmosIHNldXJhdF9jbHVzdGVycyAlaW4lIGNsdXN0ZXJzX29pKQpTZXVyYXQ6OklkZW50cyhzdWJzb2JqKSA9IHN1YnNvYmokc2FtcGxlX3R5cGUKCnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCmBgYAoKV2UgbWFrZSBpZGVudGlmeSBzcGVjaWZpYyBtYXJrZXJzIGZvciB0aGVzZSBncm91cCA6CgpgYGB7ciBkZV9JUlMsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2VycyhzdWJzb2JqLCBpZGVudC4xID0gIkhTIiwgaWRlbnQuMiA9ICJIRCIpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JQogIGRwbHlyOjphcnJhbmdlKC1hdmdfbG9nRkMsIHBjdC4xIC0gcGN0LjIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRtYXJrID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCiMjIyBVcCBpbiBIUwoKV2hhdCBhcmUgdGhlIGdlbmVzIHVwLXJlZ3VsYXRlZCBpbiBIUyA/CgpgYGB7ciBvcmFfSVJTX2hzfQpnZW5lc19vZl9pbnRlcmVzdCA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICByb3duYW1lcygpCgplbnJpY2hyX3Jlc3VsdHMgPSBhcXVhcml1czo6cnVuX2VucmljaHIoZ2VuZV9uYW1lcyA9IGdlbmVzX29mX2ludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9jb3JyZXNwID0gZ2VuZV9jb3JyZXNwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zZXRzID0gZ2VuZV9zZXRzWywgYygiZ3NfbmFtZSIsICJlbnNlbWJsX2dlbmUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWtlX3Bsb3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJEb3duLXJlZ3VsYXRlZCBpbiBIUyBjb21wYXJlZCB0byBIRCIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRlbnJpY2hyX2hzID0gZW5yaWNocl9yZXN1bHRzJGVnbwoKZW5yaWNocl9yZXN1bHRzJHBsb3QgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKYGBgCgojIyMgVXAgaW4gSEQKCldoYXQgYXJlIHRoZSBnZW5lcyB1cC1yZWd1bGF0ZWQgaW4gSEQgPwoKYGBge3Igb3JhX0lSU19oZH0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDIDwgMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiRG93bi1yZWd1bGF0ZWQgaW4gSFMgY29tcGFyZWQgdG8gSEQiKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kZW5yaWNocl9oZCA9IGVucmljaHJfcmVzdWx0cyRlZ28KCmVucmljaHJfcmVzdWx0cyRwbG90ICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCmBgYAoKCiMjIyBHU0VBCgpXZSBydW4gYSBHU0VBIGZvciBhbGwgZ2VuZSBzZXRzLCBmcm9tIHRoZSBmdWxsIGNvdW50IG1hdHJpeCA6CgpgYGB7ciBnc2VhX2luX0lSUywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0MH0KcmFua2VkX2dlbmVfbGlzdCA9IGFxdWFyaXVzOjpydW5fZm9sZGNoYW5nZShTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNvbG5hbWVzKHN1YnNvYmopW3N1YnNvYmpAYWN0aXZlLmlkZW50ICVpbiUgIkhTIl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAyID0gY29sbmFtZXMoc3Vic29iailbc3Vic29iakBhY3RpdmUuaWRlbnQgJWluJSAiSEQiXSkKbmFtZXMocmFua2VkX2dlbmVfbGlzdCkgPSBnZW5lX2NvcnJlc3AkSUQKCmdzZWFfcmVzdWx0cyA9IGFxdWFyaXVzOjpnc2VhX3J1bihyYW5rZWRfZ2VuZV9saXN0ID0gcmFua2VkX2dlbmVfbGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR1NFQV9wX3ZhbF90aHJlc2ggPSAxKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kZ3NlYSA9IGdzZWFfcmVzdWx0cwoKZ3NlYV9yZXN1bHRzQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKHB2YWx1ZSA8IDAuMDUpICU+JQogIGRwbHlyOjp0b3BfbiguLCBuID0gMjAwLCB3dCA9IGFicyhORVMpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvb19sb25nID0gaWZlbHNlKG5jaGFyKElEKSA+IDYwLCB5ZXMgPSBUUlVFLCBubyA9IEZBTFNFKSkgJT4lCiAgZHBseXI6Om11dGF0ZShJRCA9IHN0cmluZ3I6OnN0cl9zdWIoSUQsIGVuZCA9IDYwKSkgJT4lCiAgZHBseXI6Om11dGF0ZShJRCA9IGlmZWxzZSh0b29fbG9uZywgeWVzID0gcGFzdGUwKElELCAiLi4uIiksIG5vID0gSUQpKSAlPiUKICBhcXVhcml1czo6Z3NlYV9wbG90KHNob3dfbGVnZW5kID0gVFJVRSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiR1NFQSB1c2luZyBhbGwgZ2VuZXMgKGNvdW50IG1hdHJpeCkiKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKQpgYGAKCldlIGNvbXB1dGUgYSBmb2xkIGNoYW5nZSB0YWJsZSB0byBtYWtlIGEgd29yZGNsb3VkIDoKCmBgYHtyIGZvbGRfY2hhbmdlX0lSU30KZm9sZF9jaGFuZ2UgPSBnZW5lX2NvcnJlc3AKY29sbmFtZXMoZm9sZF9jaGFuZ2UpID0gYygiZ2VuZV9uYW1lIiwgIklEIikKcm93bmFtZXMoZm9sZF9jaGFuZ2UpID0gZm9sZF9jaGFuZ2UkSUQKZm9sZF9jaGFuZ2UkRkMgPSByYW5rZWRfZ2VuZV9saXN0W3Jvd25hbWVzKGZvbGRfY2hhbmdlKV0KZm9sZF9jaGFuZ2UkcGN0LjEgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpWywgc3Vic29iakBhY3RpdmUuaWRlbnQgPT0gIkhTIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJHBjdC4yID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKVssIHN1YnNvYmpAYWN0aXZlLmlkZW50ID09ICJIRCJdICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIHJldHVybiggbWVhbihvbmVfcm93ICE9IDApICkKICB9KQpmb2xkX2NoYW5nZSRGQ194X3BjdCA9IGlmZWxzZShmb2xkX2NoYW5nZSRGQyA+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IGZvbGRfY2hhbmdlJEZDICogZm9sZF9jaGFuZ2UkcGN0LjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMikKCmRpbShmb2xkX2NoYW5nZSkgOyBoZWFkKGZvbGRfY2hhbmdlKQpgYGAKCldlIG1ha2UgdGhlIGdzZWEgcGxvdCBmb3Igc29tZSBnZW5lIHNldHMgOgoKYGBge3IgZ3NlYV9wbG90X0lSUywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyNX0KZ3Nfb2kgPSBjKCJHT0JQX0tFUkFUSU5JWkFUSU9OIiwKICAgICAgICAgICJHT0JQX0NFTExfQUdHUkVHQVRJT04iLAogICAgICAgICAgIkdPQ0NfQ09STklGSUVEX0VOVkVMT1BFIiwKICAgICAgICAgICJHT0JQX01FU0VOQ0hZTUFMX0NFTExfRElGRkVSRU5USUFUSU9OIiwKICAgICAgICAgICJXUF9ETkFfUkVQQUlSX1BBVEhXQVlTX0ZVTExfTkVUV09SSyIsCiAgICAgICAgICAiSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQiLAogICAgICAgICAgIkhBTExNQVJLX0UyRl9UQVJHRVRTIikKCnBsb3RfbGlzdCA9IG1ha2VfZ3NlYV9wbG90KGdzZWFfcmVzdWx0cywgZ3Nfb2ksIGZvbGRfY2hhbmdlLCAiRkNfeF9wY3QiKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMiwxLjUpKQpgYGAKCiMjIE1lZHVsbGEKCmBgYHtyIGdyb3VwX25hbWVfbWVkdWxsYX0KZ3JvdXBfbmFtZSA9ICJtZWR1bGxhIgpgYGAKCk1lZHVsbGEgYXJlIHRob3NlIGNsdXN0ZXJzIDoKCmBgYHtyIGNsdXN0ZXJzX29pX21lZHVsbGF9CmNsdXN0ZXJzX29pID0gYXMuY2hhcmFjdGVyKHVuaXF1ZShzb2JqJHNldXJhdF9jbHVzdGVyc1tzb2JqJGNsdXN0ZXJfdHlwZSA9PSBncm91cF9uYW1lXSkpCmNsdXN0ZXJzX29pCmBgYAoKV2UgcmVwcmVzZW50IHRob3NlIGNsdXN0ZXJzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9tZWR1bGxhLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDV9CnNlZV9jbHVzdGVycygibWVkdWxsYSIpCmBgYAoKIyMjIERFCgpXZSBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gSFMgYW5kIEhELCB3aXRoaW4gdGhpcyBwb3B1bGF0aW9uIDoKCmBgYHtyIHN1YnNldF9tZWR1bGxhLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0Kc3Vic29iaiA9IHN1YnNldChzb2JqLCBzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDIpKQpTZXVyYXQ6OklkZW50cyhzdWJzb2JqKSA9IHN1YnNvYmokc2FtcGxlX3R5cGUKCnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCmBgYAoKV2UgbWFrZSBpZGVudGlmeSBzcGVjaWZpYyBtYXJrZXJzIGZvciB0aGVzZSBncm91cCA6CgpgYGB7ciBkZV9tZWR1bGxhLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc3Vic29iaiwgaWRlbnQuMSA9ICJIUyIsIGlkZW50LjIgPSAiSEQiKQoKbWFyayA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICBkcGx5cjo6YXJyYW5nZSgtYXZnX2xvZ0ZDLCBwY3QuMSAtIHBjdC4yKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kbWFyayA9IG1hcmsKCmRpbShtYXJrKQpoZWFkKG1hcmssIG4gPSAyMCkKYGBgCgojIyMgVXAgaW4gSFMKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhTIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfbWVkdWxsYV9ocywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpnZW5lc19vZl9pbnRlcmVzdCA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPiAwKSAlPiUKICByb3duYW1lcygpCgplbnJpY2hyX3Jlc3VsdHMgPSBhcXVhcml1czo6cnVuX2VucmljaHIoZ2VuZV9uYW1lcyA9IGdlbmVzX29mX2ludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9jb3JyZXNwID0gZ2VuZV9jb3JyZXNwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zZXRzID0gZ2VuZV9zZXRzWywgYygiZ3NfbmFtZSIsICJlbnNlbWJsX2dlbmUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWtlX3Bsb3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJVcC1yZWd1bGF0ZWQgaW4gSFMgY29tcGFyZWQgdG8gSEQiKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kZW5yaWNocl9ocyA9IGVucmljaHJfcmVzdWx0cyRlZ28KCmVucmljaHJfcmVzdWx0cyRwbG90ICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCmBgYAoKIyMjIFVwIGluIEhECgpXZSBleHBsb3JlIGVucmljaG1lbnQgaW4gZ2VuZSBzZXRzIGZvciBIRCBwb3B1bGF0aW9uLgoKYGBge3Igb3JhX21lZHVsbGFfaGQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDIDwgMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiRG93bi1yZWd1bGF0ZWQgaW4gSFMgY29tcGFyZWQgdG8gSEQiKQoKbGlzdF9yZXN1bHRzW1tncm91cF9uYW1lXV0kZW5yaWNocl9oZCA9IGVucmljaHJfcmVzdWx0cyRlZ28KCmVucmljaHJfcmVzdWx0cyRwbG90ICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCmBgYAoKCiMjIyBHU0VBCgpXZSBydW4gYSBHU0VBIGZvciBhbGwgZ2VuZSBzZXRzLCBmcm9tIHRoZSBmdWxsIGNvdW50IG1hdHJpeCA6CgpgYGB7ciBnc2VhX2luX21lZHVsbGEsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNDB9CnJhbmtlZF9nZW5lX2xpc3QgPSBhcXVhcml1czo6cnVuX2ZvbGRjaGFuZ2UoU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjb2xuYW1lcyhzdWJzb2JqKVtzdWJzb2JqQGFjdGl2ZS5pZGVudCAlaW4lICJIUyJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMiA9IGNvbG5hbWVzKHN1YnNvYmopW3N1YnNvYmpAYWN0aXZlLmlkZW50ICVpbiUgIkhEIl0pCm5hbWVzKHJhbmtlZF9nZW5lX2xpc3QpID0gZ2VuZV9jb3JyZXNwJElECgpnc2VhX3Jlc3VsdHMgPSBhcXVhcml1czo6Z3NlYV9ydW4ocmFua2VkX2dlbmVfbGlzdCA9IHJhbmtlZF9nZW5lX2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdTRUFfcF92YWxfdGhyZXNoID0gMSkKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGdzZWEgPSBnc2VhX3Jlc3VsdHMKCmdzZWFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAwLjA1KSAlPiUKICBkcGx5cjo6dG9wX24oLiwgbiA9IDIwMCwgd3QgPSBhYnMoTkVTKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b29fbG9uZyA9IGlmZWxzZShuY2hhcihJRCkgPiA3MCwgeWVzID0gVFJVRSwgbm8gPSBGQUxTRSkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBzdHJpbmdyOjpzdHJfc3ViKElELCBlbmQgPSA3MCkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBpZmVsc2UodG9vX2xvbmcsIHllcyA9IHBhc3RlMChJRCwgIi4uLiIpLCBubyA9IElEKSkgJT4lCiAgYXF1YXJpdXM6OmdzZWFfcGxvdChzaG93X2xlZ2VuZCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkdTRUEgdXNpbmcgYWxsIGdlbmVzIChjb3VudCBtYXRyaXgpIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpXZSBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgdGFibGUgdG8gbWFrZSBhIHdvcmRjbG91ZCA6CgpgYGB7ciBmb2xkX2NoYW5nZV9tZWR1bGxhfQpmb2xkX2NoYW5nZSA9IGdlbmVfY29ycmVzcApjb2xuYW1lcyhmb2xkX2NoYW5nZSkgPSBjKCJnZW5lX25hbWUiLCAiSUQiKQpyb3duYW1lcyhmb2xkX2NoYW5nZSkgPSBmb2xkX2NoYW5nZSRJRApmb2xkX2NoYW5nZSRGQyA9IHJhbmtlZF9nZW5lX2xpc3Rbcm93bmFtZXMoZm9sZF9jaGFuZ2UpXQpmb2xkX2NoYW5nZSRwY3QuMSA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmosIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIilbLCBzdWJzb2JqQGFjdGl2ZS5pZGVudCA9PSAiSFMiXSAlPiUKICBhcHBseSguLCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbihvbmVfcm93KSB7CiAgICByZXR1cm4oIG1lYW4ob25lX3JvdyAhPSAwKSApCiAgfSkKZm9sZF9jaGFuZ2UkcGN0LjIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpWywgc3Vic29iakBhY3RpdmUuaWRlbnQgPT0gIkhEIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJEZDX3hfcGN0ID0gaWZlbHNlKGZvbGRfY2hhbmdlJEZDID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBmb2xkX2NoYW5nZSRGQyAqIGZvbGRfY2hhbmdlJHBjdC4yKQoKZGltKGZvbGRfY2hhbmdlKSA7IGhlYWQoZm9sZF9jaGFuZ2UpCmBgYAoKV2UgbWFrZSB0aGUgZ3NlYSBwbG90IGZvciBzb21lIGdlbmUgc2V0cyA6CgpgYGB7ciBnc2VhX3Bsb3Rfd2l0aGluX21lZHVsbGEsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMjl9CmdzX29pID0gYygiR09NRl9SQUdFX1JFQ0VQVE9SX0JJTkRJTkciLAogICAgICAgICAgIkdPQ0NfR0FNTUFfU0VDUkVUQVNFX0NPTVBMRVgiLAogICAgICAgICAgIlJFQUNUT01FX05PVENIM19BQ1RJVkFUSU9OX0FORF9UUkFOU01JU1NJT05fT0ZfU0lHTkFMX1RPX1RIRV9OVUNMRVVTIiwKICAgICAgICAgICJSRUFDVE9NRV9OT05DQU5PTklDQUxfQUNUSVZBVElPTl9PRl9OT1RDSDMiLAogICAgICAgICAgIkdPQlBfUE9TSVRJVkVfUkVHVUxBVElPTl9PRl9DSEVNT0tJTkVfQ19YX0NfTU9USUZfTElHQU5EXzJfUFJPRFVDVElPTiIsCiAgICAgICAgICAiR09CUF9QT1NJVElWRV9SRUdVTEFUSU9OX09GX0hJU1RPTkVfSDNfSzI3X01FVEhZTEFUSU9OIiwKICAgICAgICAgICJHT01GX1RSQU5TRk9STUlOR19HUk9XVEhfRkFDVE9SX0JFVEFfQklORElORyIsCiAgICAgICAgICAiR09NRl9TVFJVQ1RVUkFMX01PTEVDVUxFX0FDVElWSVRZX0NPTkZFUlJJTkdfRUxBU1RJQ0lUWSIsCiAgICAgICAgICAiR09CUF9SRUdVTEFUSU9OX09GX01FU0VOQ0hZTUFMX1NURU1fQ0VMTF9ESUZGRVJFTlRJQVRJT04iKQoKcGxvdF9saXN0ID0gbWFrZV9nc2VhX3Bsb3QoZ3NlYV9yZXN1bHRzLCBnc19vaSwgZm9sZF9jaGFuZ2UsICJGQ194X3BjdCIpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMiwgd2lkdGhzID0gYygyLDEuNSkpCmBgYAoKIyMgQ29ydGV4CgpgYGB7ciBncm91cF9uYW1lX2NvcnRleH0KZ3JvdXBfbmFtZSA9ICJjb3J0ZXgiCmBgYAoKQ29ydGV4IGFyZSB0aG9zZSBjbHVzdGVycyA6CgpgYGB7ciBjbHVzdGVyc19vaV9jb3J0ZXh9CmNsdXN0ZXJzX29pID0gYXMuY2hhcmFjdGVyKHVuaXF1ZShzb2JqJHNldXJhdF9jbHVzdGVyc1tzb2JqJGNsdXN0ZXJfdHlwZSA9PSBncm91cF9uYW1lXSkpCmNsdXN0ZXJzX29pCmBgYAoKV2UgcmVwcmVzZW50IHRob3NlIGNsdXN0ZXJzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9jb3J0ZXgsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNX0Kc2VlX2NsdXN0ZXJzKCJjb3J0ZXgiKQpgYGAKCiMjIyBERQoKV2UgcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIEhTIGFuZCBIRCwgd2l0aGluIHRoaXMgcG9wdWxhdGlvbiA6CgpgYGB7ciBzdWJzZXRfY29ydGV4LCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0Kc3Vic29iaiA9IHN1YnNldChzb2JqLCBzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDApKQpTZXVyYXQ6OklkZW50cyhzdWJzb2JqKSA9IHN1YnNvYmokc2FtcGxlX3R5cGUKCnRhYmxlKHN1YnNvYmokc2FtcGxlX3R5cGUpCmBgYAoKV2UgbWFrZSBpZGVudGlmeSBzcGVjaWZpYyBtYXJrZXJzIGZvciB0aGVzZSBncm91cCA6CgpgYGB7ciBkZV9jb3J0ZXgsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2VycyhzdWJzb2JqLCBpZGVudC4xID0gIkhTIiwgaWRlbnQuMiA9ICJIRCIpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JQogIGRwbHlyOjphcnJhbmdlKC1hdmdfbG9nRkMsIHBjdC4xIC0gcGN0LjIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRtYXJrID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCgojIyMgVXAgaW4gSFMKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhTIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfY29ydGV4X2hzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDR9CmdlbmVzX29mX2ludGVyZXN0ID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA+IDApICU+JQogIHJvd25hbWVzKCkKCmVucmljaHJfcmVzdWx0cyA9IGFxdWFyaXVzOjpydW5fZW5yaWNocihnZW5lX25hbWVzID0gZ2VuZXNfb2ZfaW50ZXJlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2NvcnJlc3AgPSBnZW5lX2NvcnJlc3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ha2VfcGxvdCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIlVwLXJlZ3VsYXRlZCBpbiBIUyBjb21wYXJlZCB0byBIRCIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRlbnJpY2hyX2hzID0gZW5yaWNocl9yZXN1bHRzJGVnbwoKZW5yaWNocl9yZXN1bHRzJHBsb3QgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKYGBgCgojIyMgVXAgaW4gSEQKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhEIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfY29ydGV4X2hkLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CmdlbmVzX29mX2ludGVyZXN0ID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA8IDApICU+JQogIHJvd25hbWVzKCkKCmVucmljaHJfcmVzdWx0cyA9IGFxdWFyaXVzOjpydW5fZW5yaWNocihnZW5lX25hbWVzID0gZ2VuZXNfb2ZfaW50ZXJlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2NvcnJlc3AgPSBnZW5lX2NvcnJlc3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ha2VfcGxvdCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIkRvd24tcmVndWxhdGVkIGluIEhTIGNvbXBhcmVkIHRvIEhEIikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGVucmljaHJfaGQgPSBlbnJpY2hyX3Jlc3VsdHMkZWdvCgplbnJpY2hyX3Jlc3VsdHMkcGxvdCArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCgojIyMgR1NFQQoKV2UgcnVuIGEgR1NFQSBmb3IgYWxsIGdlbmUgc2V0cywgZnJvbSB0aGUgZnVsbCBjb3VudCBtYXRyaXggOgoKYGBge3IgZ3NlYV9pbl9jb3J0ZXgsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNDB9CnJhbmtlZF9nZW5lX2xpc3QgPSBhcXVhcml1czo6cnVuX2ZvbGRjaGFuZ2UoU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjb2xuYW1lcyhzdWJzb2JqKVtzdWJzb2JqQGFjdGl2ZS5pZGVudCAlaW4lICJIUyJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMiA9IGNvbG5hbWVzKHN1YnNvYmopW3N1YnNvYmpAYWN0aXZlLmlkZW50ICVpbiUgIkhEIl0pCm5hbWVzKHJhbmtlZF9nZW5lX2xpc3QpID0gZ2VuZV9jb3JyZXNwJElECgpnc2VhX3Jlc3VsdHMgPSBhcXVhcml1czo6Z3NlYV9ydW4ocmFua2VkX2dlbmVfbGlzdCA9IHJhbmtlZF9nZW5lX2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdTRUFfcF92YWxfdGhyZXNoID0gMSkKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGdzZWEgPSBnc2VhX3Jlc3VsdHMKCmdzZWFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAwLjA1KSAlPiUKICBkcGx5cjo6dG9wX24oLiwgbiA9IDIwMCwgd3QgPSBhYnMoTkVTKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b29fbG9uZyA9IGlmZWxzZShuY2hhcihJRCkgPiA2MCwgeWVzID0gVFJVRSwgbm8gPSBGQUxTRSkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBzdHJpbmdyOjpzdHJfc3ViKElELCBlbmQgPSA2MCkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBpZmVsc2UodG9vX2xvbmcsIHllcyA9IHBhc3RlMChJRCwgIi4uLiIpLCBubyA9IElEKSkgJT4lCiAgYXF1YXJpdXM6OmdzZWFfcGxvdChzaG93X2xlZ2VuZCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkdTRUEgdXNpbmcgYWxsIGdlbmVzIChjb3VudCBtYXRyaXgpIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpXZSBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgdGFibGUgdG8gbWFrZSBhIHdvcmRjbG91ZCA6CgpgYGB7ciBmb2xkX2NoYW5nZV9jb3J0ZXh9CmZvbGRfY2hhbmdlID0gZ2VuZV9jb3JyZXNwCmNvbG5hbWVzKGZvbGRfY2hhbmdlKSA9IGMoImdlbmVfbmFtZSIsICJJRCIpCnJvd25hbWVzKGZvbGRfY2hhbmdlKSA9IGZvbGRfY2hhbmdlJElECmZvbGRfY2hhbmdlJEZDID0gcmFua2VkX2dlbmVfbGlzdFtyb3duYW1lcyhmb2xkX2NoYW5nZSldCmZvbGRfY2hhbmdlJHBjdC4xID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKVssIHN1YnNvYmpAYWN0aXZlLmlkZW50ID09ICJIUyJdICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIHJldHVybiggbWVhbihvbmVfcm93ICE9IDApICkKICB9KQpmb2xkX2NoYW5nZSRwY3QuMiA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmosIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIilbLCBzdWJzb2JqQGFjdGl2ZS5pZGVudCA9PSAiSEQiXSAlPiUKICBhcHBseSguLCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbihvbmVfcm93KSB7CiAgICByZXR1cm4oIG1lYW4ob25lX3JvdyAhPSAwKSApCiAgfSkKZm9sZF9jaGFuZ2UkRkNfeF9wY3QgPSBpZmVsc2UoZm9sZF9jaGFuZ2UkRkMgPiAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSBmb2xkX2NoYW5nZSRGQyAqIGZvbGRfY2hhbmdlJHBjdC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubyA9IGZvbGRfY2hhbmdlJEZDICogZm9sZF9jaGFuZ2UkcGN0LjIpCgpkaW0oZm9sZF9jaGFuZ2UpIDsgaGVhZChmb2xkX2NoYW5nZSkKYGBgCgpXZSBtYWtlIHRoZSBnc2VhIHBsb3QgZm9yIHNvbWUgZ2VuZSBzZXRzIDoKCmBgYHtyIGdzZWFfcGxvdF93aXRoaW5fY29ydGV4LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDE4fQpnc19vaSA9IGMoIkdPQ0NfS0VSQVRJTl9GSUxBTUVOVCIsCiAgICAgICAgICAiR09CUF9JTlRFUk1FRElBVEVfRklMQU1FTlRfT1JHQU5JWkFUSU9OIiwKICAgICAgICAgICJXUF9DWVRPS0lORVNfQU5EX0lORkxBTU1BVE9SWV9SRVNQT05TRSIsCiAgICAgICAgICAiR09CUF9QT1NJVElWRV9SRUdVTEFUSU9OX09GX0hFVEVST1RZUElDX0NFTExfQ0VMTF9BREhFU0lPTiIsCiAgICAgICAgICAiR09CUF9SRVNQT05TRV9UT19GVU5HVVMiLAogICAgICAgICAgIlJFQUNUT01FX1ZPTFRBR0VfR0FURURfUE9UQVNTSVVNX0NIQU5ORUxTIikKCnBsb3RfbGlzdCA9IG1ha2VfZ3NlYV9wbG90KGdzZWFfcmVzdWx0cywgZ3Nfb2ksIGZvbGRfY2hhbmdlLCAiRkNfeF9wY3QiKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMiwxLjUpKQpgYGAKCiMjIEN1dGljbGUKCmBgYHtyIGdyb3VwX25hbWVfY3V0aWNsZX0KZ3JvdXBfbmFtZSA9ICJjdXRpY2xlIgpgYGAKCkN1dGljbGUgYXJlIHRob3NlIGNsdXN0ZXJzIDoKCmBgYHtyIGNsdXN0ZXJzX29pX2N1dGljbGV9CmNsdXN0ZXJzX29pID0gYXMuY2hhcmFjdGVyKHVuaXF1ZShzb2JqJHNldXJhdF9jbHVzdGVyc1tzb2JqJGNsdXN0ZXJfdHlwZSA9PSBncm91cF9uYW1lXSkpCmNsdXN0ZXJzX29pCmBgYAoKV2UgcmVwcmVzZW50IHRob3NlIGNsdXN0ZXJzIG9uIHRoZSBwcm9qZWN0aW9uIDoKCmBgYHtyIHNlZV9jdXRpY2xlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDV9CnNlZV9jbHVzdGVycygiY3V0aWNsZSIpCmBgYAoKIyMjIERFCgpXZSBwZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gSFMgYW5kIEhELCB3aXRoaW4gdGhpcyBwb3B1bGF0aW9uIDoKCmBgYHtyIHN1YnNldF9jdXRpY2xlXzEsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQpzdWJzb2JqID0gc3Vic2V0KHNvYmosIHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMSkpClNldXJhdDo6SWRlbnRzKHN1YnNvYmopID0gc3Vic29iaiRzYW1wbGVfdHlwZQoKdGFibGUoc3Vic29iaiRzYW1wbGVfdHlwZSkKYGBgCgpXZSBtYWtlIGlkZW50aWZ5IHNwZWNpZmljIG1hcmtlcnMgZm9yIHRoZXNlIGdyb3VwIDoKCmBgYHtyIGRlX2N1dGljbGVfMSwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9Cm1hcmsgPSBTZXVyYXQ6OkZpbmRNYXJrZXJzKHN1YnNvYmosIGlkZW50LjEgPSAiSFMiLCBpZGVudC4yID0gIkhEIikKCm1hcmsgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLWF2Z19sb2dGQywgcGN0LjEgLSBwY3QuMikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJG1hcmsgPSBtYXJrCgpkaW0obWFyaykKaGVhZChtYXJrLCBuID0gMjApCmBgYAoKIyMjIFVwIGluIEhTCgpXZSBleHBsb3JlIGVucmljaG1lbnQgaW4gZ2VuZSBzZXRzIGZvciBIUyBwb3B1bGF0aW9uLgoKYGBge3Igb3JhX2N1dGljbGVfaHMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiVXAtcmVndWxhdGVkIGluIEhTIGNvbXBhcmVkIHRvIEhEIikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGVucmljaHJfaHMgPSBlbnJpY2hyX3Jlc3VsdHMkZWdvCgplbnJpY2hyX3Jlc3VsdHMkcGxvdCArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCiMjIyBVcCBpbiBIRAoKV2UgZXhwbG9yZSBlbnJpY2htZW50IGluIGdlbmUgc2V0cyBmb3IgSEQgcG9wdWxhdGlvbi4KCmBgYHtyIG9yYV9jdXRpY2xlX2hkLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CmdlbmVzX29mX2ludGVyZXN0ID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2dGQyA8IDApICU+JQogIHJvd25hbWVzKCkKCmVucmljaHJfcmVzdWx0cyA9IGFxdWFyaXVzOjpydW5fZW5yaWNocihnZW5lX25hbWVzID0gZ2VuZXNfb2ZfaW50ZXJlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2NvcnJlc3AgPSBnZW5lX2NvcnJlc3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ha2VfcGxvdCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X3RpdGxlID0gIkRvd24tcmVndWxhdGVkIGluIEhTIGNvbXBhcmVkIHRvIEhEIikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGVucmljaHJfaGQgPSBlbnJpY2hyX3Jlc3VsdHMkZWdvCgplbnJpY2hyX3Jlc3VsdHMkcGxvdCArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCgojIyMgR1NFQQoKV2UgcnVuIGEgR1NFQSBmb3IgYWxsIGdlbmUgc2V0cywgZnJvbSB0aGUgZnVsbCBjb3VudCBtYXRyaXggOgoKYGBge3IgZ3NlYV9pbl9jdXRpY2xlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDQwfQpyYW5rZWRfZ2VuZV9saXN0ID0gYXF1YXJpdXM6OnJ1bl9mb2xkY2hhbmdlKFNldXJhdDo6R2V0QXNzYXlEYXRhKHN1YnNvYmosIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiY291bnRzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAxID0gY29sbmFtZXMoc3Vic29iailbc3Vic29iakBhY3RpdmUuaWRlbnQgJWluJSAiSFMiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDIgPSBjb2xuYW1lcyhzdWJzb2JqKVtzdWJzb2JqQGFjdGl2ZS5pZGVudCAlaW4lICJIRCJdKQpuYW1lcyhyYW5rZWRfZ2VuZV9saXN0KSA9IGdlbmVfY29ycmVzcCRJRAoKZ3NlYV9yZXN1bHRzID0gYXF1YXJpdXM6OmdzZWFfcnVuKHJhbmtlZF9nZW5lX2xpc3QgPSByYW5rZWRfZ2VuZV9saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zZXRzID0gZ2VuZV9zZXRzWywgYygiZ3NfbmFtZSIsICJlbnNlbWJsX2dlbmUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHU0VBX3BfdmFsX3RocmVzaCA9IDEpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRnc2VhID0gZ3NlYV9yZXN1bHRzCgpnc2VhX3Jlc3VsdHNAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIocHZhbHVlIDwgMC4wNSkgJT4lCiAgZHBseXI6OnRvcF9uKC4sIG4gPSAyMDAsIHd0ID0gYWJzKE5FUykpICU+JQogIGRwbHlyOjptdXRhdGUodG9vX2xvbmcgPSBpZmVsc2UobmNoYXIoSUQpID4gNjAsIHllcyA9IFRSVUUsIG5vID0gRkFMU0UpKSAlPiUKICBkcGx5cjo6bXV0YXRlKElEID0gc3RyaW5ncjo6c3RyX3N1YihJRCwgZW5kID0gNjApKSAlPiUKICBkcGx5cjo6bXV0YXRlKElEID0gaWZlbHNlKHRvb19sb25nLCB5ZXMgPSBwYXN0ZTAoSUQsICIuLi4iKSwgbm8gPSBJRCkpICU+JQogIGFxdWFyaXVzOjpnc2VhX3Bsb3Qoc2hvd19sZWdlbmQgPSBUUlVFKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJHU0VBIHVzaW5nIGFsbCBnZW5lcyAoY291bnQgbWF0cml4KSIpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpCmBgYAoKV2UgY29tcHV0ZSBhIGZvbGQgY2hhbmdlIHRhYmxlIHRvIG1ha2UgYSB3b3JkY2xvdWQgOgoKYGBge3IgZm9sZF9jaGFuZ2VfY3V0aWNsZX0KZm9sZF9jaGFuZ2UgPSBnZW5lX2NvcnJlc3AKY29sbmFtZXMoZm9sZF9jaGFuZ2UpID0gYygiZ2VuZV9uYW1lIiwgIklEIikKcm93bmFtZXMoZm9sZF9jaGFuZ2UpID0gZm9sZF9jaGFuZ2UkSUQKZm9sZF9jaGFuZ2UkRkMgPSByYW5rZWRfZ2VuZV9saXN0W3Jvd25hbWVzKGZvbGRfY2hhbmdlKV0KZm9sZF9jaGFuZ2UkcGN0LjEgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpWywgc3Vic29iakBhY3RpdmUuaWRlbnQgPT0gIkhTIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJHBjdC4yID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKVssIHN1YnNvYmpAYWN0aXZlLmlkZW50ID09ICJIRCJdICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIHJldHVybiggbWVhbihvbmVfcm93ICE9IDApICkKICB9KQpmb2xkX2NoYW5nZSRGQ194X3BjdCA9IGlmZWxzZShmb2xkX2NoYW5nZSRGQyA+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IGZvbGRfY2hhbmdlJEZDICogZm9sZF9jaGFuZ2UkcGN0LjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMikKCmRpbShmb2xkX2NoYW5nZSkgOyBoZWFkKGZvbGRfY2hhbmdlKQpgYGAKCldlIG1ha2UgdGhlIGdzZWEgcGxvdCBmb3Igc29tZSBnZW5lIHNldHMgOgoKYGBge3IgZ3NlYV9wbG90X3dpdGhpbl9jdXRpY2xlLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQpnc19vaSA9IGMoIkdPTUZfVF9DRUxMX1JFQ0VQVE9SX0JJTkRJTkciLAogICAgICAgICAgIkhBTExNQVJLX1BFUk9YSVNPTUUiLAogICAgICAgICAgIkdPQlBfVE9MTF9MSUtFX1JFQ0VQVE9SXzNfU0lHTkFMSU5HX1BBVEhXQVkiKQoKcGxvdF9saXN0ID0gbWFrZV9nc2VhX3Bsb3QoZ3NlYV9yZXN1bHRzLCBnc19vaSwgZm9sZF9jaGFuZ2UsICJGQ194X3BjdCIpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gMiwgd2lkdGhzID0gYygyLDEuNSkpCmBgYAoKIyBHbG9iYWwgdmlzdWFsaXphdGlvbgoKV2UgZXh0cmFjdCBhbGwgREUgZ2VuZXMgOgoKYGBge3IgcHJlcF9nZW5lc30KZmVhdHVyZXNfb2kgPSBsYXBwbHkobGlzdF9yZXN1bHRzLCBgW1tgLCAxKSAlPiUKICBsYXBwbHkoLiwgRlVOID0gcm93bmFtZXMpICU+JQogIHVubGlzdCgpICU+JSB1bm5hbWUoKSAlPiUgdW5pcXVlKCkKCmxlbmd0aChmZWF0dXJlc19vaSkKYGBgCgojIyBIZWF0bWFwCgpXZSBwcmVwYXJlIHRoZSBzY2FsZWQgZXhwcmVzc2lvbiBtYXRyaXggOgoKYGBge3IgbWF0fQptYXRfZXhwcmVzc2lvbiA9IFNldXJhdDo6R2V0QXNzYXlEYXRhKHNvYmosIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiZGF0YSIpW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByZXNzaW9uID0gTWF0cml4Ojp0KG1hdF9leHByZXNzaW9uKQptYXRfZXhwcmVzc2lvbiA9IGR5bnV0aWxzOjpzY2FsZV9xdWFudGlsZShtYXRfZXhwcmVzc2lvbikgIyBiZXR3ZWVuIDAgYW5kIDEKbWF0X2V4cHJlc3Npb24gPSBNYXRyaXg6OnQobWF0X2V4cHJlc3Npb24pCm1hdF9leHByZXNzaW9uID0gYXMubWF0cml4KG1hdF9leHByZXNzaW9uKSAjIG5vdCBzcGFyc2UKCmRpbShtYXRfZXhwcmVzc2lvbikKYGBgCgpXZSBwcmVwYXJlIHRoZSBoZWF0bWFwIGFubm90YXRpb24gOgoKYGBge3IgaGFfdG9wfQpoYV90b3AgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgY2VsbF90eXBlID0gc29iaiRjbHVzdGVyX3R5cGUsCiAgc2FtcGxlX3R5cGUgPSBzb2JqJHNhbXBsZV90eXBlLAogIGNsdXN0ZXIgPSBzb2JqJHNldXJhdF9jbHVzdGVycywKICBjb2wgPSBsaXN0KGNlbGxfdHlwZSA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICBzYW1wbGVfdHlwZSA9IHNldE5hbWVzKG5tID0gYygiSFMiLCAiSEQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiI0M1NUY0MCIsICIjMkM3OEU2IikpLAogICAgICAgICAgICAgY2x1c3RlciA9IHNldE5hbWVzKG5tID0gbGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcXVhcml1czo6Z2dfY29sb3JfaHVlKGxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKSkpKSkKYGBgCgpBbmQgdGhlIGhlYXRtYXAgOgoKYGBge3IgaGVhdG1hcCwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA0MH0Kc29iaiRjZWxsX2dyb3VwID0gcGFzdGUwKHNvYmokY2x1c3Rlcl90eXBlLCBzb2JqJHNhbXBsZV90eXBlKSAlPiUKICBhcy5mYWN0b3IoKQoKaHQgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChtYXRfZXhwcmVzc2lvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBhcXVhcml1czo6Y29sb3JfY252LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQW5ub3RhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JvdXBpbmcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjphcnJhbmdlKGNsdXN0ZXJfdHlwZSwgc2FtcGxlX3R5cGUsIHNldXJhdF9jbHVzdGVycykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9zcGxpdCA9IHNvYmokY2VsbF9ncm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fZ2FwID0gcmVwKHVuaXQoYygwLjAxLCAyKSwgIm1tIiksIDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFZpc3VhbCBhc3BlY3QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCiMjIFZpb2xpbiBwbG90cwoKYGBge3IgcGxvdF9saXN0X3ZsbiwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA4MH0KcGxvdF9saXN0ID0gbGFwcGx5KHNvcnQoZmVhdHVyZXNfb2kpLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgc3BsaXQuYnkgPSAic2FtcGxlX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBvbmVfZ2VuZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwoYnJlYWtzID0gYygiSFMiLCAiSEQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiNDNTVGNDAiLCAiIzJDNzhFNiIpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBzb3J0KGZlYXR1cmVzX29pKQpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gNikKYGBgCgojIEdhbW1hIHNlY3JldGFzZQoKR2FtbWEgc2VjcmF0YXNlIGNvZGluZy1nZW5lcyBhcmUgZW5yaWNoZWQgaW4gdGhlIGNvcnRleCwgaW4gSFMgY29tcGFyZWQgdG8gSEQuIFdlIG1ha2UgYSBzY29yZSBmb3IgYWxsIGNlbGxzIGZvciB0aGUgYXNzb2NpYXRlZCBnZW5lIHNldC4KCmBgYHtyIGdhbW1hX3NlY3JldGFzZV9nZW5lX3NldH0KdGhlX2dzX25hbWUgPSAiR09DQ19HQU1NQV9TRUNSRVRBU0VfQ09NUExFWCIKdGhlX2NvbnRlbnQgPSBnZW5lX3NldHMgJT4lCiAgZHBseXI6OmZpbHRlcihnc19uYW1lID09IHRoZV9nc19uYW1lKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX3N5bWJvbCkgJT4lCiAgdW5pcXVlKCkKCnNvYmokc2NvcmVfZ2FtbWEgPSBTZXVyYXQ6OkFkZE1vZHVsZVNjb3JlKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbGlzdCh0aGVfY29udGVudCkpJENsdXN0ZXIxCgp0aGVfY29udGVudApgYGAKCldlIGxvb2sgYXQgdGhlIHNjb3JlIHNwbGl0IGJ5IGNlbGwgdHlwZSBhbmQgc2FtcGxlIHR5cGUuCgpgYGB7ciBnYW1tYV9zZWNyZXRhc2VfdmxuLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KU2V1cmF0OjpWbG5QbG90KHNvYmosIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIHNwbGl0LmJ5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gInNjb3JlX2dhbW1hIiwgcHQuc2l6ZSA9IDAuMDAxKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwoYnJlYWtzID0gYygiSFMiLCAiSEQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiNDNTVGNDAiLCAiIzJDNzhFNiIpKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMgU2F2ZQoKV2Ugc2F2ZSB0aGUgbGlzdCBvZiByZXN1bHRzIDoKCmBgYHtyIHNhdmVfbGlzdF9yZXN1bHRzfQpzYXZlUkRTKGxpc3RfcmVzdWx0cywgZmlsZSA9IHBhc3RlMChvdXRfZGlyLCAiLyIsIHNhdmVfbmFtZSwgIl9saXN0X3Jlc3VsdHMucmRzIikpCmBgYAoKCiMgUiBTZXNzaW9uCgpgYGB7ciBzZXNzaW9uaW5mbywgZWNobyA9IEZBTFNFLCBmb2xkX291dHB1dCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCgo=